package jeconbond.automata.behaviour.antstrat;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import jeconbond.automata.INode;
import jeconbond.automata.IOutcome;
import jeconbond.automata.AutomataUtils;
import jeconbond.automata.behaviour.IWalkStrategy;
import jeconbond.economic.production.IResourceNecessityInformer;
import jeconbond.economic.resources.ResourceBasket;
import jeconbond.economic.resources.ResourceStorrage;
import jeconbond.economic.resources.ResourceUtils;
import laboratoryQ.computation.IRandom;
import laboratoryQ.BaseUtils;

public class AntWalkStrategy implements IWalkStrategy, IResourceNecessityInformer {
	private FeramonStorrage feramonStorrage;
	private IRandom random;
	private Map<INode, Integer> out2perspForks;
	/**
	 * This is anti-pattern: state, cozed by random.
	 */
	private IOutcome choosenOut = null;
	private ResourceStorrage inputRS;
	private ResourceStorrage outputRS;
	private INode state;

	public AntWalkStrategy(
			INode root,
			FeramonStorrage feramonStorrage,
			ResourceStorrage inputRS,
			ResourceStorrage outputRS,
			IRandom random,
			boolean useBranchesEuristic
	) {
		this.feramonStorrage = feramonStorrage;
		this.random = random;
		this.inputRS = inputRS;
		this.outputRS = outputRS;
		state = root;
		if (useBranchesEuristic) {
			out2perspForks = AutomataUtils.dfsPerspectiveForksCount(root);
			if (out2perspForks == null) throw new RuntimeException("Unexpected.");
		}
	}

	static { System.out.println("\nPERFORMANCE store single buffer"); }

	@Override
	public IOutcome doWalk(INode state) {
		if (ResourceUtils.checkIfResourcesEnought_unsafe(inputRS, choosenOut)) {
			this.state = choosenOut.getDestination();
			ResourceUtils.processOut(inputRS, outputRS, choosenOut);
			return choosenOut;
		} else {
			this.state = state; 
			return null;
		}
	}

	@Override
	public List<InfoItem> listInfoItems() {
		chooseOutcome(state);
		List<InfoItem> result = new ArrayList<InfoItem>();
		for (ResourceBasket rb : choosenOut.getRequirements()) {
			result.add(
					new InfoItem(rb, 0)
			);
		}
		return result;
	}

	private void chooseOutcome(INode state) {
		int n = state.getOutcomes().size();
		double[] buf = new double[n];
		for (int i = 0; i < n; i++) {
			IOutcome out = state.getOutcomes().get(i);
			buf[i] = feramonStorrage.computeFeramon(out);
			buf[i] *= getPerspectBranches(out);
		}
		int position = BaseUtils.weightedRandom(buf, random);
		IOutcome out = state.getOutcomes().get(position);
		choosenOut = out;
	}

	private double getPerspectBranches(IOutcome out) {
		if (out2perspForks == null) {
			return 1.0;
		} else {
			return out2perspForks.get(out.getDestination());
		}
	}
}

